package com.tbit.uqbike.client.service.impl;

import cn.hutool.core.date.DateUtil;
import com.tbit.common.enums.FeeDayTypeEnum;
import com.tbit.common.enums.FeeTypeEnum;
import com.tbit.uqbike.client.constant.AccountConfigExtConstant;
import com.tbit.uqbike.client.constant.TimeFeeConstant;
import com.tbit.uqbike.client.dao.core.AccountConfigExtDao;
import com.tbit.uqbike.client.dao.core.AdAccountFeeDao;
import com.tbit.uqbike.client.dao.core.TimeFeeDao;
import com.tbit.uqbike.client.dao.core.VipFeeDao;
import com.tbit.uqbike.client.pojo.AdAccountFee;
import com.tbit.uqbike.client.pojo.Machine;
import com.tbit.uqbike.client.pojo.VipCardLog;
import com.tbit.uqbike.client.pojo.newEntity.AccountConfigExt;
import com.tbit.uqbike.client.pojo.newEntity.TimeFee;
import com.tbit.uqbike.client.pojo.vo.FeeCommon;
import com.tbit.uqbike.client.service.AdAccountFeeService;
import com.tbit.uqbike.client.service.CacheService;
import com.tbit.uqbike.client.service.MachineFeeService;
import com.tbit.uqbike.client.service.VipCardLogService;
import com.tbit.uqbike.client.util.DateTimeUtil;
import com.tbit.uqbike.client.util.HolidayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: chen
 * @Description:
 * @Date: 创建于 17:19 2020/1/10
 */
@Service("adAccountFeeService")
public class AdAccountFeeServiceImpl implements AdAccountFeeService {
    @Autowired
    private CacheService cacheService;
    @Autowired
    private AdAccountFeeDao adAccountFeeDao;
    @Autowired
    private MachineFeeService machineFeeService;
    @Autowired
    private VipFeeDao vipFeeDao;
    @Autowired
    private VipCardLogService vipCardLogService;
    @Resource
    private TimeFeeDao timeFeeDao;
    @Resource
    private AccountConfigExtDao accountConfigExtDao;

    @Override
    public AdAccountFee getByAccountId(Integer accountId, Integer machineId) {
        return cacheService.getAdAccountFee(accountId, machineId);
    }

    /**
     * 查询区域计费规则中，当前hour命中 的 type类型的计费规则
     * @param accountId 区域id
     * @param hour 小时
     * @param attachId 附加id
     * @param type 见 @see TimeFeeConstant
     * @return
     */
    @Override
    public TimeFee getByType(Integer accountId, Integer hour, Integer attachId,Integer type) {
        Map<String,Object> map = new HashMap<>();
        map.put("accountId",accountId);
        map.put("time",hour);
        map.put("attachId",attachId);
        map.put("type",type);
        return adAccountFeeDao.getByType(map);
    }

    @Override
    public AdAccountFee getFee(Integer accountId, Integer machineId, String startTime) {
        AdAccountFee adAccountFee=null ;
        Integer hour = DateUtil.hour(DateUtil.parse(startTime),true);
        // 车辆绑定计费
        if (machineId != null) {
            adAccountFee = machineFeeService.getByAccountId(accountId, machineId);
            adAccountFee=check(accountId,hour,adAccountFee, TimeFeeConstant.TIMEFEE_MACHINEFEE);
            if (Objects.nonNull(adAccountFee)){
                return adAccountFee;
            }
        }
        FeeTypeEnum feeTypeEnum = queryFeeType(accountId);
        // 区域默认计费
        adAccountFee = adAccountFeeDao.getByAccountId(accountId);
        if (Objects.equals(feeTypeEnum,FeeTypeEnum.TIME_FEE)) {
            // 区域计费 / 分时
            adAccountFee=check(accountId,hour,adAccountFee, TimeFeeConstant.TIMEFEE_ACCOUNTFEE);
        }
        // 假日计费模式
        if (Objects.equals(feeTypeEnum,FeeTypeEnum.HOLIDAY_FEE)) {
            // 假日计费模式下，前端角标默认是工作日
            adAccountFee.setFeeType(FeeDayTypeEnum.WORKDAY.toString());

            List<TimeFee> allTimeFees = adAccountFeeDao.getTimeFeeByType(accountId);
            List<TimeFee> timeFees = new ArrayList<>();
            String day = HolidayUtils.getDateValue(startTime, "day");
            String dayOfWeek = HolidayUtils.getDateValue(startTime,"dayOfWeek");
            // 假日模式-普通计费
            timeFees = allTimeFees.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_AREA_FEE,TimeFeeConstant.HOLIDAY_AREA_TIME_FEE).contains(x.getType())).sorted(Comparator.comparingInt(TimeFee::getType)).collect(Collectors.toList());
            // 假日模式-周末计费 ，是周末且非调休
            if (!HolidayUtils.validWorkOnWeekend(day) && (Objects.equals(dayOfWeek,String.valueOf(DayOfWeek.SATURDAY)) || Objects.equals(dayOfWeek,String.valueOf(DayOfWeek.SUNDAY))) ){
                // 升序排序保证默认计费在前
                timeFees = allTimeFees.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_WEEKEND_FEE,TimeFeeConstant.HOLIDAY_WEEKEND_TIME_FEE).contains(x.getType())).sorted(Comparator.comparingInt(TimeFee::getType)).collect(Collectors.toList());
                // 前端角标展示
                adAccountFee.setFeeType(FeeDayTypeEnum.WEEKEND.toString());
            }
            // 假日模式-假日计费
            if (HolidayUtils.validHoliday(day)) {
                // 过滤节假日的分时计费 ， 升序排序保证默认计费在前
                timeFees = allTimeFees.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_FEE,TimeFeeConstant.HOLIDAY_TIME_FEE).contains(x.getType())).sorted(Comparator.comparingInt(TimeFee::getType)).collect(Collectors.toList());
                adAccountFee.setFeeType(FeeDayTypeEnum.HOLIDAY.toString());
            }
            for(TimeFee timeFee:timeFees){
                if(timeFee.getBeginTime() <= hour && hour < timeFee.getEndTime()){
                    BeanUtils.copyProperties(timeFee,adAccountFee,"feeType");
                }
            }
        }
        return adAccountFee;
    }

    /**
     * 查询当前生效的计费模式
     * @param accountId 区域id
     * @return FeeTypeEnum
     * @see FeeTypeEnum
     */
    public FeeTypeEnum queryFeeType(Integer accountId) {
        FeeTypeEnum res = FeeTypeEnum.NORMAL;
        // 查询区域所有配置
        List<AccountConfigExt> accountConfigExtList = accountConfigExtDao.getByAccountId(accountId);
        // 分时计费是否启用判断
        boolean time_fee = accountConfigExtList.stream().anyMatch(x -> Objects.equals(x.getParamKey(), AccountConfigExtConstant.ACCOUNTCONFIG_TIMEOFUSEBILLING) && Objects.equals(x.getParamValue(), "1"));
        if (time_fee){
            res = FeeTypeEnum.TIME_FEE;
        }
        // 假日计费是否启用判断
        boolean holiday_fee = accountConfigExtList.stream().anyMatch(x -> Objects.equals(x.getParamKey(), AccountConfigExtConstant.HOLIDAY_FEE_SWITCH) && Objects.equals(x.getParamValue(), "1"));
        if (holiday_fee){
            res = FeeTypeEnum.HOLIDAY_FEE;
        }
        return res;
    }

    @Override
    public AdAccountFee getVipFee(Integer accountId, String startTime, Integer attachId) {
        Integer hour = DateUtil.hour(DateUtil.parse(startTime),true);
        TimeFee timeFee =getByType(accountId,hour,attachId,TimeFeeConstant.TIMEFEE_VIPFEE);
        AdAccountFee adAccountFee= vipFeeDao.getByVipId(attachId);
        if(timeFee!=null&&adAccountFee!=null){
            adAccountFee.setBaseTime(timeFee.getBaseTime());
            adAccountFee.setBaseTimeFee(timeFee.getBaseTimeFee());
            adAccountFee.setOverTime(timeFee.getOverTime());
            adAccountFee.setOverTimeFee(timeFee.getOverTimeFee());
            adAccountFee.setBaseMile(timeFee.getBaseMile());
            adAccountFee.setBaseMileFee(timeFee.getBaseMileFee());
            adAccountFee.setOverMile(timeFee.getOverMile());
            adAccountFee.setOverMileFee(timeFee.getOverMileFee());
            adAccountFee.setCapTime(timeFee.getCapTime());
            adAccountFee.setCapTimeFee(timeFee.getOverTimeFee());
            adAccountFee.setCapMile(timeFee.getCapMile());
            adAccountFee.setCapMileFee(timeFee.getCapMileFee());
            adAccountFee.setFreeTime(timeFee.getFreeTime());
            adAccountFee.setFreeDistance(timeFee.getFreeDistance());
            adAccountFee.setFreeConditionType(timeFee.getFreeConditionType());
        }
        return adAccountFee;
    }

    public List<FeeCommon> handleTimeFee(AdAccountFee adAccountFee,List<TimeFee> timeFees){
        List<FeeCommon> fees=new ArrayList<>();
        FeeCommon feeCommon = new FeeCommon();
        BeanUtils.copyProperties(adAccountFee,feeCommon);
        fees.add(feeCommon);
        for(TimeFee timeFee:timeFees){
            FeeCommon feeCommon1 = new FeeCommon();
            BeanUtils.copyProperties(timeFee,feeCommon1);
            fees.add(feeCommon1);
        }
        return fees;
    }

    @Override
    public List<FeeCommon> getTimeFeeByAccountId(Machine machine, Integer userId) {
        List<FeeCommon> fees =new ArrayList<>();
        AdAccountFee adAccountFee = adAccountFeeDao.getByAccountId(machine.getAccountId());
        if(adAccountFee==null){
            return new ArrayList<>();
        }
        List<TimeFee> timeFees = adAccountFeeDao.getTimeFeeByType(machine.getAccountId());
        /*根据类型分组*/
        Map<Integer, List<TimeFee>> map = timeFees.stream().collect(Collectors.groupingBy(TimeFee::getType));
        //区域计费
        if(map.get(TimeFeeConstant.TIMEFEE_ACCOUNTFEE)!=null){
            fees= handleTimeFee(adAccountFee,map.get(TimeFeeConstant.TIMEFEE_ACCOUNTFEE));
        }
        //车辆计费
        AdAccountFee machineFee = machineFeeService.getByAccountId(machine.getAccountId(), machine.getMachineId());
        if(machineFee!=null){
            List<TimeFee> list=new ArrayList<>();
            if(map.get(TimeFeeConstant.TIMEFEE_MACHINEFEE)!=null){
               list = map.get(TimeFeeConstant.TIMEFEE_MACHINEFEE).stream().filter(timeFee -> machineFee.getFeeId().equals(timeFee.getAttachId())).collect(Collectors.toList());
            }
            fees=handleTimeFee(machineFee,list);
        }

        //会员卡计费
        Integer feeId = Optional.of(adAccountFee).filter(o -> null != o.getName()).map(AdAccountFee::getFeeId).orElseGet(() -> 0);
        VipCardLog vipCardLog =vipCardLogService.getUserVip(machine.getAccountId(), userId, feeId);
        if(vipCardLog!=null){
            AdAccountFee vipFee =vipFeeDao.getByVipId(vipCardLog.getVipId());
            List<TimeFee> list;
            if(map.get(TimeFeeConstant.TIMEFEE_VIPFEE)!=null){
                //筛选已经购买的会员卡的分时段计费规则
               list = map.get(TimeFeeConstant.TIMEFEE_MACHINEFEE).stream().filter(timeFee -> vipCardLog.getFeeId().equals(timeFee.getAttachId())).collect(Collectors.toList());
               fees=handleTimeFee(vipFee,list);
            }
        }
        // 判断假日计费
        FeeTypeEnum feeTypeEnum = queryFeeType(machine.getAccountId());
        String day = HolidayUtils.getDateValue(DateTimeUtil.getNowTime(), "day");
        if (Objects.equals(feeTypeEnum,FeeTypeEnum.HOLIDAY_FEE) && HolidayUtils.validHoliday(day)) {
            // 升序排序保证默认计费在前
            List<TimeFee> holidayFees = timeFees.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_FEE,TimeFeeConstant.HOLIDAY_TIME_FEE).contains(x.getType())).sorted(Comparator.comparingInt(TimeFee::getType)).collect(Collectors.toList());
            fees = holidayFees.stream().map(x -> {
                FeeCommon feeCommon = new FeeCommon();
                BeanUtils.copyProperties(x, feeCommon);
                return feeCommon;
            }).collect(Collectors.toList());
        }
        return fees;
    }

    /**
     * 获取分时计费
     * @param accountId 区域id
     * @param type 计费类型 0 区域计费、1 车辆计费、2 会员卡计费
     * @return List<TimeFee>
     */
    @Override
    public List<TimeFee> getTimeFee(Integer accountId, Integer type, Integer attachId) {
        List<TimeFee> timeFee = timeFeeDao.getTimeFee(accountId, type, attachId);
        // 区域默认计费
        AdAccountFee adAccountFee = adAccountFeeDao.getByAccountId(accountId);
        if (Objects.nonNull(adAccountFee)){
            TimeFee timeFee1 = new TimeFee();
            BeanUtils.copyProperties(adAccountFee, timeFee1);
            timeFee.add(timeFee1);
        }
        // 查询假日计费规则
        List<AccountConfigExt> accountConfigExtList = accountConfigExtDao.getByAccountId(accountId);
        boolean holidayModel = accountConfigExtList.stream().anyMatch(x -> Objects.equals(x.getParamKey(), AccountConfigExtConstant.HOLIDAY_FEE_SWITCH) && Objects.equals(x.getParamValue(), "1"));
        if (holidayModel) {
            String nowTime = DateTimeUtil.getNowTime();
            String day = HolidayUtils.getDateValue(nowTime,"day");
            String dayOfWeek = HolidayUtils.getDateValue(nowTime,"dayOfWeek");
            List<TimeFee> allTimeFees = adAccountFeeDao.getTimeFeeByType(accountId);
            boolean isWeekend = Objects.equals(dayOfWeek, String.valueOf(DayOfWeek.SATURDAY)) || Objects.equals(dayOfWeek, String.valueOf(DayOfWeek.SUNDAY));
            if (HolidayUtils.validHoliday(day)) {
                timeFee = allTimeFees.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_FEE, TimeFeeConstant.HOLIDAY_TIME_FEE).contains(x.getType())).sorted(Comparator.comparingInt(TimeFee::getType)).collect(Collectors.toList());
            }else if (isWeekend) {
                timeFee = allTimeFees.stream().filter(x -> Arrays.asList(TimeFeeConstant.HOLIDAY_WEEKEND_FEE, TimeFeeConstant.HOLIDAY_WEEKEND_TIME_FEE).contains(x.getType())).sorted(Comparator.comparingInt(TimeFee::getType)).collect(Collectors.toList());
            }
        }
        return timeFee;
    }


    /**
     * 分时段计费规则校验
     *
     * @param accountId 区域id
     * @param hour 时刻
     * @param adAccountFee 计费规则
     * @param type 类型
     * @return
     */
    public AdAccountFee check(Integer accountId, Integer hour,AdAccountFee adAccountFee,Integer type){
        if(adAccountFee!=null){
            TimeFee timeFee=getByType(accountId,hour,adAccountFee.getFeeId(), type);
            if(timeFee != null){
                BeanUtils.copyProperties(timeFee,adAccountFee);
            }
            // 前端角标展示
            adAccountFee.setFeeType(FeeDayTypeEnum.WORKDAY.toString());
        }
        return adAccountFee;
    }
}
